package groovy.runtime.metaclass.aconcagua.measure

import aconcagua.measure.DividedUnit;
import aconcagua.measure.MultipliedUnit;
import aconcagua.measure.NullUnit;
import aconcagua.measure.SimpleUnit;
import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;


class MultipliedUnitMetaClass extends DelegatingMetaClass {
	
	MultipliedUnitMetaClass(MetaClass aMetaClass) {
		super(aMetaClass);
	}
	
	@Override
	public Object invokeConstructor(Object[] arguments) {
		return arguments.size() == 1 ? unitUsing(arguments[0]) : unitUsing(arguments[0], arguments[1]);
	}
	
	// (class) instance creation - private
	private unitUsing(DividedUnit aUnit, DividedUnit anotherUnit) {
		return (aUnit.numerator() * anotherUnit.numerator()) /
			(aUnit.denominator() * anotherUnit.denominator());
	}
	
	private unitUsing(DividedUnit aUnit, MultipliedUnit anotherUnit) {
		return new MultipliedUnit(anotherUnit, aUnit);
	}
	
	private unitUsing(DividedUnit aUnit, SimpleUnit anotherUnit) {
		return new MultipliedUnit(anotherUnit, aUnit);
	}
	
	private unitUsing(MultipliedUnit aUnit, DividedUnit anotherUnit) {
		return (aUnit * anotherUnit.numerator()) / anotherUnit.denominator();
	}
	
	private unitUsing(MultipliedUnit aUnit, MultipliedUnit anotherUnit) {
		def factors = aUnit.factors();
		factors.addAll(anotherUnit.factors());
		return new MultipliedUnit(factors);
	}
	
	private unitUsing(MultipliedUnit aUnit, SimpleUnit anotherUnit) {
		return new MultipliedUnit(anotherUnit, aUnit);
	}
	
	private unitUsing(SimpleUnit aUnit, DividedUnit anotherUnit) {
		return (aUnit * anotherUnit.numerator()) / anotherUnit.denominator();
	}
	
	private unitUsing(SimpleUnit aUnit, MultipliedUnit anotherUnit) {
		def factors = anotherUnit.factors();
		factors.add(aUnit);
		
		return new MultipliedUnit(factors);
	}
	
	private unitUsing(SimpleUnit aUnit, SimpleUnit anotherUnit) {
		return new MultipliedUnit([aUnit, anotherUnit]);
	}
	
	private unitUsing(ArrayList aFactors) {
		if (aFactors.size() == 0) return new NullUnit();
		if (aFactors.size() == 1) return aFactors.first();
		return super.invokeConstructor(aFactors);
	}
}
